package cn._2dland.websocket.model.handshake;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import cn._2dland.util.EndChecker;
import cn._2dland.websocket.exception.HandshakeException;

/**
 * WebSocket握手请求
 * 
 * @author deadblue
 */
public class HandShakeRequest {

	/** 行结束符 */
	private static final String CRLF = "\r\n";
	
	/** 请求头：升级 */
	public static final String HEADER_UPGRADE = "Upgrade";
	/** 请求头：连接 */
	public static final String HEADER_CONNECTION = "Connection";
	/** 请求头：主机 */
	public static final String HEADER_HOST = "Host";
	/** 请求头：来源地址（草稿版） */
	public static final String HEADER_ORIGIN_DARFT = "Origin";
	/** 请求头：来源地址 */
	public static final String HEADER_ORIGIN = "Sec-WebSocket-Origin";
	/** 请求头：Cookie数据 */
	public static final String HEADER_COOKIE = "Cookie";
	/** 请求头：版本号 */
	public static final String HEADER_VERSION = "Sec-WebSocket-Version";
	/** 请求头：握手密钥 */
	public static final String HEADER_KEY = "Sec-WebSocket-Key";
	/** 请求头：握手密钥1（v7.6草稿版） */
	public static final String HEADER_KEY1 = "Sec-WebSocket-Key1";
	/** 请求头：握手密钥2（v7.6草稿版） */
	public static final String HEADER_KEY2 = "Sec-WebSocket-Key2";
	/** 请求头：协议 */
	public static final String HEADER_PROTOCOL = "Sec-WebSocket-Protocol";
	/** 请求头：扩展信息 */
	public static final String HEADER_EXTENSIONS = "Sec-WebSocket-Extensions";

	/** 请求路径 */
	private String path = null;
	/** 请求头 */
	private Map<String, String> headers = new HashMap<String, String>();
	/** 负载内容（v7.6草稿版会传入密钥3） */
	private byte[] payload = null;

	/** 是否为草稿版 */
	private boolean draft = false;

	private HandShakeRequest() {}

	/**
	 * 从数据流读取，并填充对应属性
	 * 
	 * @param in
	 * @throws Exception
	 */
	private void readFromInputStream(InputStream in) throws Exception {
		ByteArrayOutputStream buf = new ByteArrayOutputStream();
		do {
			int b = in.read();
			if(b < 0) break;
			buf.write(b);
		} while(in.available() > 0);
		buf.close();

		parse(buf.toByteArray());
	}
	
	/**
	 * 解析原始数据
	 * 
	 * @param data
	 */
	private void parse(byte[] data) throws Exception {

		// 拆分出头部与负载数据
		EndChecker checker = new EndChecker(new byte[]{0x0d, 0x0a, 0x0d, 0x0a});
		int len = -1;
		for(int i = 0; i < data.length; i++) {
			if(checker.isEnd(data[i])) {
				len = i + 1;
				break;
			}
		}
		if(len < 0)
			throw new HandshakeException(HandshakeException.REASON_INVALID_REQUEST);

		// 前半为头部数据
		byte[] headerData = new byte[len];
		System.arraycopy(data, 0, headerData, 0, len);
		// 后半为负载数据
		payload = new byte[data.length - len];
		System.arraycopy(data, len, payload, 0, data.length - len);

		// 解析头部数据
		String headerString = new String(headerData, "utf-8");
		String[] lines = headerString.split(CRLF);
		// 第一行为路径信息，调用parsePath方法解析
		parsePath(lines[0]);
		// 从第二行开始都是请求头信息，直接解析
		for(int i = 0; i < lines.length; i++) {
			// 跳过空行
			if(lines[i].length() == 0) continue;

			// 解析每行数据
			int pos = lines[i].indexOf(": ");
			if(pos < 0) continue;

			// 解析出请求头的名称和内容
			String name = lines[i].substring(0, pos);
			String value = lines[i].substring(pos + 2);

			// 保存
			headers.put(name, value);
		}

		// 判断是否为草稿版本
		draft = headers.containsKey(HEADER_KEY1);
	}

	/**
	 * 解析请求路径，并存储在path变量中
	 * 
	 * @param line
	 */
	private void parsePath(String line) {
		// 去掉结尾的HTTP/1.1
		line = line.replaceAll("\\sHTTP/\\d\\.\\d$", "");
		// 去掉开头的GET
		line = line.replaceFirst("^GET\\s", "").trim();

		path = line;
	}

	/**
	 * 协议是否为v7.6草稿版，返回false表示是v13正式版
	 * 
	 * @return
	 */
	public boolean isDraft() {
		return draft;
	}

	/**
	 * 获取指定请求头内容
	 * 
	 * @param name
	 * @return
	 */
	public String getHeader(String name) {
		return headers.get(name);
	}

	/**
	 * 负载数据
	 * 
	 * @return
	 */
	public byte[] getPayload() {
		return payload;
	}

	/**
	 * 来源地址
	 * 
	 * @return
	 */
	public String getOrigin() {
		String origin = headers.get(HEADER_ORIGIN_DARFT);
		if(origin == null)
			origin = headers.get(HEADER_ORIGIN);
		
		return origin;
	}
	
	/**
	 * 返回请求地址
	 * 
	 * @return
	 */
	public String getLocation() {
		StringBuffer buf = new StringBuffer();
		buf.append("ws://").append(headers.get(HEADER_HOST)).append(path);
		return buf.toString();
	}
	
	/**
	 * 从输入流读取一个握手请求
	 * 
	 * @param in 输入流
	 * @return
	 */
	public static HandShakeRequest read(InputStream in) throws Exception {
		HandShakeRequest request = new HandShakeRequest();
		request.readFromInputStream(in);

		return request;
	}
	
}
